/*
	Vector.h

		Vector manipulation.

	Author:	Brett Porter
	Email: brettporter@yahoo.com
	Website: http://rsn.gamedev.net/pl3d
	Copyright (C)2000, 2001, Brett Porter. All Rights Reserved.
	This source code is released under the LGPL. See license.txt for details.

	Created: 9 December 2000
	Last Edited: 8 January 2001

	Please see the file ChangeLog.html for a revision history.
*/

#pragma once

#include <assert.h>
#include "Matrix.h"


namespace _FOGGE_
{
	namespace _UTIL_
	{
		
		namespace _MATH_
		{
			class Vector
			{
				public:
					/*	Constructor. (0, 0, 0, 1) */
					Vector();

					/*	Constructor. 3 float values. */
					Vector( const float *vector );

					Vector( float x,float y, float z,float w=0 );

					/*	Destructor. */
					~Vector();

					/*	Retrieve vector. */
					const float *getVector() const { return m_vector; }

					/*	Transform the vector by a matrix. */
					void transform( const Matrix& m );

					/*	Transform the vector by a matrix, not including the scaling or transformation elements (use only top-left 3x3 of matrix). */
					void transform3( const Matrix& m );

					/*	Set the values of the vector. Takes 3 float values. */
					void set( const float *vector );

					/*	Set the values of the vector. Takes 3 float values. */
					void set( const Vector& vector );


					/*	Set the values of the vector */
					void set( float x,float y,float z );

					/*	Translate by another vector. */
					void add( const Vector& v );

					/*	Reset to (0, 0, 0, 1). */
					void reset();

					/*	Get the length of the vector. */
					float length() const;

					/*	Normalize (make it a unit vector). */
					void normalize();

					const float operator[]( int index ) const { return m_vector[index]; }


					bool isZero()	const
					{
						if(x==0 || y==0 || z==0 )	return false;
						return true;
					}

					/**
					\brief negation
					*/
					Vector operator -() const;
					/**
					\brief vector addition
					*/
					Vector operator +(const Vector & v) const;
					/**
					\brief vector difference
					*/
					Vector operator -(const Vector & v) const;
					/**
					\brief scalar post-multiplication
					*/
					Vector operator *(float f) const;
					/**
					\brief scalar division
					*/
					Vector operator /(float f) const;
					/**
					\brief vector addition
					*/
					Vector& operator +=(const Vector& v);
					/**
					\brief vector difference
					*/
					Vector& operator -=(const Vector& v);
					/**
					\brief scalar multiplication
					*/
					Vector& operator *=(float f);
					/**
					\brief scalar division
					*/
					Vector& operator /=(float f);
					Vector& operator *=(const Vector& v);
					Vector& operator /=(const Vector& v);
/**
					\brief cross product
					*/
					Vector cross(const Vector& v) const;

					/**
					\brief cross product
					*/
					Vector operator^(const Vector& v) const;
					/**
					\brief dot product
					*/
					float   operator|(const Vector& v) const;
					float	dot(const Vector& v) const;


					/**
					\brief Assignment operator.
					*/
					const Vector& operator=(const Vector& v);

					/**
					\brief Assignment operator.
					*/
					const Vector& operator=(float* v);
					


				private:
					//	Vector data
					union 
					{
					    float m_vector[4];
						struct
						{
							float x;
							float y;
							float z;
							float w;
						};
					};
			};

			extern Vector DIRECTION_FRONT,DIRECTION_RIGHT,DIRECTION_UP;


			inline Vector::Vector( float x,float y, float z,float w )
			{
				m_vector[0] = x;
				m_vector[1] = y;
				m_vector[2] = z;
				m_vector[3] = w;
			}
			inline void Vector::reset()
			{
				m_vector[0] = m_vector[1] = m_vector[2] = 0;
				m_vector[3] = 1;
			}

			inline void Vector::set( const float *values )
			{
				m_vector[0] = values[0];
				m_vector[1] = values[1];
				m_vector[2] = values[2];
			}

			inline void Vector::set( float x,float y,float z )
			{
				m_vector[0] = x;
				m_vector[1] = y;
				m_vector[2] = z;
			}
			inline void Vector::set( const Vector& vector )
			{
				m_vector[0] = vector.x;
				m_vector[1] = vector.y;
				m_vector[2] = vector.z;
			}

			inline void Vector::add( const Vector& v )
			{
				m_vector[0] += v.m_vector[0];
				m_vector[1] += v.m_vector[1];
				m_vector[2] += v.m_vector[2];
				m_vector[3] += v.m_vector[3];
			}

			inline void Vector::normalize()
			{
				float len = length();

				m_vector[0] /= len;
				m_vector[1] /= len;
				m_vector[2] /= len;
			}

			/**
			\brief negation
			*/
			inline Vector Vector::operator -() const
			{
				return Vector(-x,-y,-z,-w);
			}
			/**
			\brief vector addition
			*/
			inline Vector Vector::operator +(const Vector & v) const
			{
				return Vector(x+v.x,y+v.y,z+v.z,w+v.w);
			}
			/**
			\brief vector difference
			*/
			inline Vector Vector::operator -(const Vector & v) const
			{
				return Vector(x-v.x,y-v.y,z-v.z,w-v.w);
			}
			/**
			\brief scalar post-multiplication
			*/
			inline Vector Vector::operator *(float f) const
			{
				return Vector(x*f,y*f,z*f,w*f);
			}
			/**
			\brief scalar division
			*/
			inline Vector Vector::operator /(float f) const
			{
				assert(f&&"f is zero!!!");
				return Vector(x/f,y/f,z/f,w/f);
			}
			/**
			\brief vector addition
			*/
			inline Vector& Vector::operator +=(const Vector& v)
			{
				x+=v.x;
				y+=v.y;
				z+=v.z;
				w+=v.w;
				return *this;
			}
			/**
			\brief vector difference
			*/
			inline Vector& Vector::operator -=(const Vector& v)
			{
				x-=v.x;
				y-=v.y;
				z-=v.z;
				w-=v.w;
				return *this;
			}
			/**
			\brief scalar multiplication
			*/
			inline Vector& Vector::operator *=(const Vector& v)
			{
				x*=v.x;
				y*=v.y;
				z*=v.z;
				w*=v.w;
				return *this;
			}
			/**
			\brief scalar division
			*/
			inline Vector& Vector::operator /=(const Vector& v)
			{
				x/=v.x;
				y/=v.y;
				z/=v.z;
				w/=v.w;
				return *this;
			}
			
			/**
			\brief cross product
			*/
			inline Vector Vector::cross(const Vector& v) const
			{
				// temps needed in case left or right is this.
				float a = (y * v.z) - (z * v.y);
				float b = (z * v.x) - (x * v.z);
				float c = (x * v.y) - (y * v.x);

				return Vector(a,b,c);
			}

			/**
			\brief cross product
			*/
			inline Vector Vector::operator^(const Vector& v) const
			{
				Vector temp(x,y,z);
				return temp.cross(v);
			}
			/**
			\brief dot product
			*/
			inline float Vector::operator|(const Vector& v) const
			{
				return x*v.x + y*v.y + z*v.z + w*v.w;
			}


			/**
			\brief dot product
			*/
			inline float Vector::dot(const Vector& v) const
			{
				return x*v.x + y*v.y + z*v.z + w*v.w;
			}


			inline const Vector& Vector::operator=(const Vector& v)
			{
				x = v.x;
				y = v.y;
				z = v.z;
				return *this;
			}

			inline const Vector& Vector::operator=(float* v)
			{
				x = v[0];
				y = v[1];
				z = v[2];
				return *this;
			}
			
		}
	}
}


